package com.demo.user.exception;

import com.demo.user.config.AppEnv;
import com.demo.user.response.Response;
import com.demo.user.response.ResponseFactory;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;

/**
 * @author xiexiaohu
 */
//主要配合@ExceptionHandler使用，统一处理异常情况。
//1、@RestControllerAdvice 注解，可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute，并应用到所有@RequestMapping中。
//2、@RestControllerAdvice 是组件注解，他使得其实现类能够被classpath扫描自动发现，如果应用是通过MVC命令空间或MVC Java编程方式配置，那么该特性默认是自动开启的。
@Component
@RestControllerAdvice
public class ControllerExceptionHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(ControllerExceptionHandler.class);

    @Resource
    private AppEnv appEnv;

    @Autowired
    BindingResultExceptionHandler bindingResultExceptionHandler;

    @Autowired
    BeanValidatorExceptionHandler beanValidatorExceptionHandler;

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }

    @ExceptionHandler(BizServiceException.class)
    public Response<?> matchBizServiceException(final HttpServletRequest request, BizServiceException e) {
        Response<?> error;
        String msg = e.getFullMsg();

        error = ResponseFactory.toErrorWithException(e.getError().getErrorCode(), msg, e, appEnv.isProdEnv());
        LOGGER.debug("BizServiceException handled: {}", msg, e);
        return error;
    }


    @ExceptionHandler(ValidateException.class)
    public Response<?> matchValidateException(final HttpServletRequest request, final HandlerMethod handler,
                                              final ValidateException e) {
        String formId = request.getHeader(Constant.X_FORM_ID);
        return bindingResultExceptionHandler.buildErrorDto(e, handler, formId);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public Response<?> matchConstraintViolationException(final HttpServletRequest request, final HandlerMethod handler,
                                                         final ConstraintViolationException e) {
        String formId = request.getHeader(Constant.X_FORM_ID);
        return beanValidatorExceptionHandler.buildErrorDto(e, handler, formId);
    }

    @ExceptionHandler(AuthException.class)
    public Response<?> matchAuthException(final HttpServletRequest request, final AuthException e) {
        String msg = e.getFullMsg();

        LOGGER.debug("AuthException handled: {}", msg, e);
        return ResponseFactory.toError(msg);
    }

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public Response<?> matchMaxUploadSizeExceededException(final HttpServletRequest request, final MaxUploadSizeExceededException e) {

        String msg = "上传的图片大小不能超过5mb";
        LOGGER.debug("MaxUploadSizeExceededException handled: {}", msg, e);
        return ResponseFactory.toError(msg);
    }

    @ExceptionHandler(WotuRuntimeException.class)
    public Response<?> matchWotuRuntimeException(final HttpServletRequest request, final WotuRuntimeException e) {
        LOGGER.debug("WotuRuntimeException handled: {}", e.getMessage(), e);
        return ResponseFactory.toError(e.getMessage());
    }

    /**
     * Bad Request 错误
     */
    @ExceptionHandler(BaseException.class)
    public Response matchBaseException(BaseException e) {
        return Response.error(e.getError().getErrorCode(), e.getFullMsg());
    }

    @ExceptionHandler(Exception.class)
    public Response<?> matchException(final Exception e) {
        Response<?> error = ResponseFactory.toErrorWithException(e, appEnv.isProdEnv());
        if (StringUtils.equals(e.getClass().getSimpleName(), ClientAbortException.class.getName())) {
            LOGGER.warn("Client abort exception");

        } else {
            LOGGER.error("Unknown exception handled, msg: {}:", e.getMessage(), e);
        }
        return error;
    }
}
